/*
 * // +-------------------------------------------------------------------------------------------------
 * // |                 有你就好 [ 有节骨乃坚，无心品自端 ]     <http://encoding.wang>
 * // +-------------------------------------------------------------------------------------------------
 * // |                             独在异乡为异客         每逢佳节倍思亲
 * // +-------------------------------------------------------------------------------------------------
 * // |                 联系:   <707069100@qq.com>      <http://weibo.com/513778937>
 * // +-------------------------------------------------------------------------------------------------
 */

// -----------------------------------------------------------------------------------------------------
// +----------------------------------------------------------------------------------------------------
// |                   ErYang出品 属于小极品          共同学习    共同进步
// +----------------------------------------------------------------------------------------------------
// -----------------------------------------------------------------------------------------------------


package wang.encoding.mroot.admin.common.shiro


import org.apache.shiro.authc.credential.CredentialsMatcher
import org.apache.shiro.cache.CacheManager
import org.apache.shiro.realm.AuthorizingRealm
import org.apache.shiro.subject.PrincipalCollection
import org.slf4j.Logger
import org.slf4j.LoggerFactory
import java.util.*


/**
 * 使 OperatorAuthorizingRealmWithRpn 支持复杂表达式（使用逆波兰表达式计算）
 *
 */
abstract class OperatorAuthorizingRealmWithRpn : AuthorizingRealm {

    companion object {

        private val logger: Logger = LoggerFactory.getLogger(OperatorAuthorizingRealmWithRpn::class.java)

        // -------------------------------------------------------------------------------------------------

        private const val serialVersionUID = 5200736791708215701L

        // 支持的运算符和运算符优先级
        private val expMap: HashMap<String, Int> = object : HashMap<String, Int>() {

            init {
                val zero = 0
                val one = 1
                put("not", zero)
                put("!", zero)

                put("and", zero)
                put("&&", zero)

                put("or", zero)
                put("||", zero)

                put("(", one)
                put(")", one)
            }

        }

        // -------------------------------------------------------------------------------------------------

        private val expSet: Set<String>? = initExpList()

        // -------------------------------------------------------------------------------------------------

        private fun initExpList(): Set<String>? {
            return if (!expMap.isEmpty()) {
                expMap.keys
            } else {
                null
            }
        }

        // -------------------------------------------------------------------------------------------------

        /**
         * 计算逆波兰
         *
         * @param expList expList
         * @param exp     exp
         * @return true/false
         */
        private fun computeRpn(expList: Collection<String>, exp: Collection<String>): Boolean {
            val stack = Stack<Boolean>()
            for (temp: String in exp) {
                if (expList.contains(temp)) {
                    if ("!" == temp || "not" == temp) {
                        stack.push(!stack.pop())
                    } else if ("and" == temp || "&&" == temp) {
                        val s1: Boolean = stack.pop()
                        val s2: Boolean = stack.pop()
                        stack.push(s1 && s2)
                    } else {
                        val s1: Boolean = stack.pop()
                        val s2: Boolean = stack.pop()
                        stack.push(s1 || s2)
                    }
                } else {
                    stack.push(java.lang.Boolean.valueOf(temp))
                }
            }
            if (1 < stack.size) {
                val stringBuilder = StringBuilder()
                if (!exp.isEmpty()) {
                    val expStr: String = exp.toString()
                    stringBuilder.append(expStr)
                }
                throw RuntimeException(stringBuilder.toString())
            } else {
                return stack.pop()
            }
        }

        // -------------------------------------------------------------------------------------------------

        /**
         * 获得逆波兰表达式
         *
         * @param expList expList
         * @param exp     exp
         * @return Stack<String>
         */
        private fun getExp(expList: Collection<String>, exp: String): Stack<String> {
            val s1 = Stack<String>()
            val s2 = Stack<String>()
            for (str: String in exp.split(" ".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()) {
                val strL: String = str.trim { it <= ' ' }.toLowerCase()
                if ("" == str) {
                    continue
                }
                if ("(" == str) {
                    //左括号
                    s1.push(str)
                } else if (")" == str) {
                    //右括号
                    while (!s1.empty()) {
                        val temp: String = s1.pop()
                        if ("(" == temp) {
                            break
                        } else {
                            s2.push(temp)
                        }
                    }
                } else if (expList.contains(strL)) {
                    //操作符
                    if (s1.empty()) {
                        s1.push(strL)
                    } else {
                        val temp: String = s1.peek()
                        if ("(" == temp || ")" == temp) {
                            s1.push(strL)
                        } else if (expMap[strL]!! >= expMap[temp]!!) {
                            s1.push(strL)
                        } else {
                            s2.push(s1.pop())
                            s1.push(strL)
                        }
                    }
                } else {
                    //运算数
                    s2.push(str)
                }
            }
            while (!s1.empty()) {
                s2.push(s1.pop())
            }
            return s2
        }

        // -------------------------------------------------------------------------------------------------

    }

    // -------------------------------------------------------------------------------------------------

    constructor()

    // -------------------------------------------------------------------------------------------------

    constructor(cacheManager: CacheManager) : super(cacheManager)

    // ------------------------------------------------------------------------

    constructor(matcher: CredentialsMatcher) : super(matcher)

    // -------------------------------------------------------------------------------------------------

    constructor(cacheManager: CacheManager, matcher: CredentialsMatcher) : super(cacheManager, matcher)

    // -------------------------------------------------------------------------------------------------

    /**
     * 重写
     *
     * @param principals principals
     * @param permission permission
     * @return true/false
     */
    override fun isPermitted(principals: PrincipalCollection, permission: String): Boolean {
        val exp: Stack<String> = getExp(expSet!!, permission)
        if (1 == exp.size) {
            return super.isPermitted(principals, exp.pop())
        }
        val expTemp = ArrayList<String>()
        // 将其中的权限字符串解析成 true  false
        for (temp: String in exp) {
            if (expSet.contains(temp)) {
                expTemp.add(temp)
            } else {
                expTemp.add(java.lang.Boolean.toString(super.isPermitted(principals, temp)))
            }
        }
        // 计算逆波兰
        return computeRpn(expSet, expTemp)
    }

    // -------------------------------------------------------------------------------------------------

}

// -----------------------------------------------------------------------------------------------------

// End OperatorAuthorizingRealmWithRpn class

/* End of file OperatorAuthorizingRealmWithRpn.kt */
/* Location: ./src/main/kotlin/wang/encoding/mroot/admin/common/shiro/OperatorAuthorizingRealmWithRpn.kt */

// -----------------------------------------------------------------------------------------------------
// +----------------------------------------------------------------------------------------------------
// |                           ErYang出品 属于小极品  O(∩_∩)O~~   共同学习    共同进步
// +----------------------------------------------------------------------------------------------------
// -----------------------------------------------------------------------------------------------------
